package metricx

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"
	"unicode/utf8"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/collectors"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

type MetricPrometheusHandle func(*MetricPrometheus)

func WithCounterVec(c prometheus.CounterOpts, labelsName []string) MetricPrometheusHandle {
	return func(p *MetricPrometheus) {
		p.requestTotal = prometheus.NewCounterVec(
			c,
			labelsName,
		)
	}
}
func WithHistogramVec(c prometheus.HistogramOpts, labelsName []string) MetricPrometheusHandle {
	return func(p *MetricPrometheus) {
		p.requestDuration = prometheus.NewHistogramVec(
			c,
			labelsName,
		)
	}
}

type MetricPrometheus struct {
	registry        *prometheus.Registry
	requestTotal    *prometheus.CounterVec
	requestDuration *prometheus.HistogramVec
	httpHandler     http.Handler
}

func NewMetricPrometheus(hls ...MetricPrometheusHandle) MetricPrometheus {
	registry := prometheus.NewRegistry()
	p := MetricPrometheus{
		registry: registry,
	}
	for _, hl := range hls {
		hl(&p)
	}
	return p
}

func (p *MetricPrometheus) WrapRegistererWith(labels prometheus.Labels) prometheus.Registerer {
	return prometheus.WrapRegistererWith(labels, p.registry)
}

func (p *MetricPrometheus) MustRegister(cs ...prometheus.Collector) {
	for _, c := range cs {
		p.registry.MustRegister(c)
	}
	p.registry.MustRegister(p.requestDuration)
	p.registry.MustRegister(p.requestTotal)
	p.registry.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
	p.registry.MustRegister(collectors.NewGoCollector())
	p.httpHandler = promhttp.InstrumentMetricHandler(p.registry, promhttp.HandlerFor(p.registry, promhttp.HandlerOpts{}))
}

// ServeHTTP 用于HTTP服务
func (p *MetricPrometheus) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
	p.httpHandler.ServeHTTP(resp, req)
}

func (p *MetricPrometheus) MiddleWare(next http.Handler) http.HandlerFunc {
	return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		if strings.Contains(request.RequestURI, "metrics") {
			p.ServeHTTP(writer, request)
			return
		} //忽略
		requestUrl, err := url.ParseRequestURI(request.RequestURI)
		if err != nil {
			return
		}
		uri := requestUrl.Path
		if !utf8.ValidString(uri) {
			fmt.Printf("uri: %s not utf-8", uri)
			return
		}
		before := time.Now()
		next.ServeHTTP(writer, request)
		p.requestDuration.With(ArrToKv(uri, "appCode")).
			Observe(float64(time.Since(before) / time.Millisecond))

		requestTotalLabels := []string{
			uri,
		}

		p.requestTotal.With(ArrToKv(requestTotalLabels...)).Inc()

		//// add user metrics for api-server
		//p.userTotal.With(p.label(LabelUser, httpheader.GetUser(r.Header), LabelOrigin,
		//	getOrigin(r.Header))).Inc()
	})
}

func ArrToKv(arg ...string) prometheus.Labels {
	lenx := len(arg)
	if lenx == 0 {
		return nil
	}
	labels := make(prometheus.Labels, lenx/2)
	for i := 1; i < lenx; i += 2 {
		labels[arg[i-1]] = arg[i]
	}
	return labels
}
